package start

import (
	"gitee.com/liumou_site/logger"
	"gitee.com/liumou_site/tencent-cloud-ssl-automatic/module/AppSsl"
	"gitee.com/liumou_site/tencent-cloud-ssl-automatic/module/AppStruct"
	"gitee.com/liumou_site/tencent-cloud-ssl-automatic/module/app"
	"gitee.com/liumou_site/tencent-cloud-ssl-automatic/module/domain"
	"gitee.com/liumou_site/tencent-cloud-ssl-automatic/module/login"
	"time"
)

// auth 函数用于根据域名配置和SSL信息，寻找合适的登录认证方式。
// 参数 d 是域名配置，包含待认证的域名信息。
// 参数 sl 是SSL对象，包含多种登录认证方式的列表。
// 返回值是找到的登录认证方式和一个布尔值，表示是否找到了匹配的认证方式。
func auth(d *AppStruct.DomainConfig, sl *app.SSL) (app.LoginStruct, bool) {
	// 遍历SSL对象中的所有登录认证方式。
	for _, al := range sl.LoginList {
		// 检查当前登录认证方式是否启用，且其认证类型与域名配置中的认证类型匹配。
		if al.Enable && al.Auth == d.Auth {
			// 如果当前登录配置启用，则尝试使用此配置进行证书申请。
			return al, true
		}
	}
	// 如果没有找到匹配的登录认证方式，返回空的登录结构体和false。
	return app.LoginStruct{}, false
}

// GetDomainInfo 检查指定域名的证书信息，确定是否需要申请新证书。
// 参数:
//
//	lg: 包含用户登录信息的结构体。
//	domainName: 需要检查的域名名称。
//	count: 指定检查的证书有效期天数。
//
// 返回值:
//
//	new: 是否需要申请新证书。
//	need: 是否需要执行进一步操作。
//	err: 错误信息，如果执行过程中遇到错误。
func GetDomainInfo(lg app.LoginStruct, domainName string, count int) (new, need bool, oldId string, err error) {
	// 初始化返回值。
	need = false
	new = false
	oldId = ""
	var countdown bool

	// 获取所有SSL信息。
	all, err := AppSsl.GetSSLInfoAll(lg.SecretId, lg.SecretKey)
	if err != nil {
		logger.Warn(err)
		need = true
		return new, need, oldId, err
	}

	// 获取域名ID列表。
	list, err := AppSsl.GetDomainIdList(all)
	if err != nil {
		logger.Warn(err)
		new = true
		return new, need, oldId, err
	}

	// 遍历域名列表，查找指定域名的信息。
	for _, v := range list {
		info, errList := AppSsl.GetSSLInfoDomainInId(lg.SecretId, lg.SecretKey, v)
		if errList != nil {
			continue
		}

		// 获取域名名称。
		dn, errN := AppSsl.GetDomainNameById(lg.SecretId, lg.SecretKey, v)
		if errN != nil {
			return new, need, oldId, err
		}

		// 如果找到指定的域名。
		logger.Info("开始检查证书信息: ", dn)
		logger.Info("开始对比: ", domainName)
		if dn == domainName {
			oldId = v
			logger.Info("找到证书信息: ", dn)
			// 检查证书有效期。
			countdown, err = AppSsl.AnalysisCountdown(info, count)
			if err != nil {
				logger.Error(err)
				need = true
				return new, need, oldId, err
			}

			// 如果证书有效期充足，无需申请新证书。
			if !countdown {
				logger.Info("证书有效期充足，无需申请新证书")
				return new, need, oldId, err
			}
			logger.Info("证书需要更新: ", dn)
			new = true
			need = true
			return new, need, oldId, err
		}
	}

	// 如果未找到指定的域名或证书需要更新。
	logger.Info("未找到指定的域名或证书需要更新")
	return new, need, oldId, err
}

// Run 函数是程序的主要执行逻辑入口。
// 它接受一个指向 app.SSL 结构的指针作为参数，用于处理 SSL 证书的申请流程。
// Run函数是证书申请流程的入口，负责协调登录服务、域名配置解析和证书申请等步骤。
// 参数sl是一个指向app.SSL结构的指针，包含了登录服务和域名配置所需的必要信息。
func Run(sl *app.SSL, countdown int) {
	var NewId, jsonData, OldId string
	var need bool
	var err error

	// 初始化登录服务，为证书申请流程做准备。
	login.Start(sl.LoginDir, sl)

	// 读取并解析域名配置文件，获取所有域名的配置信息。
	dc := domain.ReadDomainTomlAll(sl.DomainDir)

	logger.Info("开始申请证书")
	// 遍历所有域名配置，为每个域名申请证书。
	for _, v := range dc {
		loginInfo, res := auth(&v, sl)
		if !res {
			// 如果没有找到匹配的登录认证方式，记录错误日志并继续下一个域名。
			logger.Error("没有找到匹配的登录认证方式: ", v.DomainName, " = ", v.Auth)
			continue
		}
		// 调用 GetDomainInfo 函数获取域名信息。
		res, need, OldId, err = GetDomainInfo(loginInfo, v.DomainName, countdown)
		if err != nil {
			logger.Warn(err)
			continue
		}
		// 如果当前证书的有效期充足，则无需更新证书。
		if !res {
			logger.Info("证书有效期充足，无需申请新证书: ", v.DomainName)
			continue
		}
		logger.Info("开始申请证书: ", v.DomainName)
		NewId, err = AppSsl.ApplyCertificate(loginInfo.SecretId, loginInfo.SecretKey, &v, OldId)
		if err != nil {
			logger.Error(err)
			continue
		}
		logger.Info("申请证书成功: ", v.DomainName)
		// 如果需要更新SSL证书信息
		if need {
			// 循环尝试获取并处理SSL证书信息，直到成功或遇到错误
			for {
				// 获取SSL证书信息
				jsonData, err = AppSsl.GetSSLInfoDomainInId(loginInfo.SecretId, loginInfo.SecretKey, NewId)
				// 如果发生错误，记录错误信息并退出
				if err != nil {
					logger.Error(err)
					return
				}
				// 尝试签发SSL证书
				err = AppSsl.SignIssue(jsonData)
				// 如果签发失败，等待15秒后重试
				if err != nil {
					logger.Debug("等待15秒后继续获取SSL证书签发状态....")
					time.Sleep(time.Second * 15)
					continue
				}
				// 签发成功，跳出循环
				break
			}
			// 更新CDN/COS的SSL证书
			err = AppSsl.UpdateSSLToCosCdn(loginInfo.SecretId, loginInfo.SecretKey, OldId, NewId)
			// 如果更新失败，记录错误信息并退出
			if err != nil {
				logger.Error(err)
				return
			}
			// 记录更新成功日志
			logger.Info("更新CDN/COS证书成功: ", v.DomainName)
		} else {
			// 如果不需要更新，记录无需更新日志
			logger.Info("无需更新CDN/COS证书: ", v.DomainName)
		}
	}
}
